package com.hanoiwiki.dao.hibernate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;

import com.hanoiwiki.dao.GenericDAO;
import com.hanoiwiki.exception.DataAccessException;
import com.hanoiwiki.model.BaseObject;


/**
 
 * @param <T>
 * @param <PK>
 */
public abstract class HibernateBaseDAO<T,PK> extends JpaDaoSupport implements GenericDAO<T,PK>  {
	private Log log = LogFactory.getLog(getClass().getName());
	private Class<T> persistenceClass;
	
	public HibernateBaseDAO(Class<T> persistenceClass) {
		this.persistenceClass = persistenceClass;
	}
	
	/**
	 * build buildQueryObject
	 * 
	 * @param selectClause
	 * @param model
	 * @return
	 */
	protected abstract QueryObject buildQueryObject(String selectClause, T model);
	
	@Override
	public T get(PK id) throws DataAccessException {
        T model = null;
        try {
        	model = (T) getJpaTemplate().find(persistenceClass, id);
		} catch (Exception e) {
			log.error(e);
			throw new DataAccessException(e);
		}
        return model;
	}//end T get(PK id)
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public List<T> getList(T model) throws DataAccessException {
    	try {
            final QueryObject queryObject = buildQueryObject(null, model);
            final BaseObject modelObject = (BaseObject) model;
            return (List<T>) getJpaTemplate().execute(new JpaCallback() {
    			public Object doInJpa(EntityManager em) throws PersistenceException {
    				Query query = em.createQuery(queryObject.getQueryString());
    			    for (String paramName : queryObject.getParameters().keySet()) {
    			    	Object paramValue = queryObject.getParameters().get(paramName);
    			    	query.setParameter(paramName, paramValue);
    			    }		
					if (modelObject.getItemsPerPage() > 0) {
						query.setFirstResult((int) modelObject.getStartIndex());
						query.setMaxResults((int) modelObject.getItemsPerPage());
					}
    			    return query.getResultList();
    			}
            });
        } catch (Exception e) {
        	log.error(e);
            throw new DataAccessException(e);
        }         		
	}//end getList(T model)
	
	@Override
	public void save(T model) throws DataAccessException {
	    try {
	    	getJpaTemplate().persist(model);
	    	getJpaTemplate().flush();
		} catch (Exception e) {
        	log.error(e);
            throw new DataAccessException(e);
		}
	}//end save(T model)
	
	
	@Override
	public void update(T model) throws DataAccessException {
	    try {
	    	getJpaTemplate().merge(model);   
	    	getJpaTemplate().flush();
		} catch (Exception e) {
        	log.error(e);
            throw new DataAccessException(e);
		}
	}//end save(T model)
	
	@Override
	public void remove(PK id) throws DataAccessException {
        try {
        	getJpaTemplate().remove(get(id));
			getJpaTemplate().flush();
		} catch (Exception e) {
        	log.error(e);
            throw new DataAccessException(e);
		}
	}//end remove(PK id)
	
	
	/**
	 * 
	 * query object 	
	 *
	 */
	class QueryObject {
		private String mQueryString;
		private Map<String, Object> mParameters = new HashMap<String, Object>();
		public QueryObject(String queryString, Map<String, Object> parameters) {
			mQueryString = queryString;
			mParameters = parameters;
		}
		public String getQueryString() {
			return mQueryString;
		}
		public void setQueryString(String queryString) {
			mQueryString = queryString;
		}
		public Map<String, Object> getParameters() {
			return mParameters;
		}
		public void setParameters(Map<String, Object> parameters) {
			mParameters = parameters;
		}
		
	}
}
